package lang

import (
	"archive/tar"
	"compress/gzip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
)

const (
	logTarGz    = "[压缩.tar.gz]"
	logDecTarGz = "[解压.tar.gz]"

	TarGzExt = ".tar.gz"
)

//	TarGz 压缩为 .tar.gz。
//	重复执行将覆盖已存在的压缩文件。
//	忽略隐藏文件夹和隐藏文件。
//	如果压缩文件非 .tar.gz，则自动补全。
//
//	param	files	待压缩文件或文件夹	eg:	/log	or	/log/file1.txt	+	/log/file2.txt
//	param	dest	压缩文件存储路径	eg:	/log/file.tar.gz
//	return	lang err
//
// code example:
//
//	var files []*os.File
//	f1, _ := os.Open("/log/file1.txt")
//	f2, _ := os.Open("/log/file2.txt")
//	files.append(files, f1)
//	files.append(files, f2)
//	TarGz(files, "dest")
func TarGz(files []*os.File, dest string) error {
	// 检查目标目录
	destDir := filepath.Dir(dest)
	if !FSIsExist(destDir) {
		FSMkdirAll(destDir)
	}
	// 补全压缩文件
	if !strings.HasSuffix(dest, TarGzExt) {
		dest = dest + TarGzExt
	}

	d, _ := os.Create(dest)
	defer d.Close()

	gw := gzip.NewWriter(d)
	defer gw.Close()

	tw := tar.NewWriter(gw)
	defer tw.Close()

	for _, file := range files {
		fmt.Printf("%s 开始压缩文件 [file=%s]\n", logTarGz, file.Name())
		err := doTarGz(file, "", tw)
		if err != nil {
			fmt.Printf("%s 压缩失败 [file=%s] [err=%s]\n", logTarGz, file.Name(), err.Error())
			return err
		}
	}
	return nil
}

// doTarGz 执行.tar.gz压缩
func doTarGz(file *os.File, prefix string, tw *tar.Writer) error {
	info, err := file.Stat()
	if err != nil {
		return err
	}
	// 忽略隐藏文件/夹
	if infoName := info.Name(); strings.HasPrefix(infoName, ".") {
		return nil
	}
	// 对目录和文件分别处理
	if info.IsDir() {
		prefix = prefix + "/" + info.Name()
		fileInfos, err := file.ReadDir(-1)
		if err != nil {
			return err
		}
		for _, fi := range fileInfos {
			f, err := os.Open(file.Name() + "/" + fi.Name())
			if err != nil {
				return err
			}
			// 压缩子目录文件
			err = doTarGz(f, prefix, tw)
			if err != nil {
				return err
			}
		}
	} else {
		header, err := tar.FileInfoHeader(info, "")
		if err != nil {
			return err
		}
		header.Name = prefix + "/" + header.Name

		err = tw.WriteHeader(header)
		if err != nil {
			return err
		}

		_, err = io.Copy(tw, file)
		file.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

// UnTarGz 解压缩.tar.gz
//
//	param	tarFile	待压缩文件路径	eg:	/log/file.tar.gz
//	param	dest	压缩文件存储路径
//	return	string xx
//	return	err .
//
// code example
//
//	tarFile := "/root/t2.tar.gz"
//	dest := "/root/t2"
//	UnTarGz(tarFile, dest)
func UnTarGz(tarFile, dest string) (string, error) {
	if !FSIsExist(dest) {
		FSMkdirAll(dest)
	}

	srcFile, err := os.Open(tarFile)
	if err != nil {
		return "", err
	}
	defer srcFile.Close()

	gr, err := gzip.NewReader(srcFile)
	if err != nil {
		return "", err
	}
	defer gr.Close()

	tr := tar.NewReader(gr)
	for {
		hdr, err := tr.Next()
		if err != nil {
			if err == io.EOF {
				break
			} else {
				return "", err
			}
		}
		filename := dest + hdr.Name
		file, err := os.Create(filename)
		if err != nil {
			return "", err
		}
		io.Copy(file, tr)
		file.Close()
	}
	return "", nil
}
